package DensityObjects
{
	import TaskStructure.DensityItem;
	
	import flash.display.Graphics;
	import flash.display.MovieClip;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.text.StaticText;

	/** An object of this class displays a liquid-filled container.  The amount of initial liquid, as a percentage of total
	 * height is set on init.  The beaker essentially has a front and a back, in which water is in both.  Objects
	 * that are added to the beaker are in-between the front and back. */
	public class Beaker extends MovieClip
	{
		public static const HRATIO:Number = .25;
		public static const MAX_OBJECTS:uint = 2; 
		
		public static var FLUID_DENSITY:Number = 1; 
		
		private static const NUM_BACKGROUND_OBJECTS:uint = 2;
		
		private var _objects:Array;
		private var _front:Shape;
		private var _back:Shape;
		private var _waterFront:Shape;
		private var _waterBack:Shape;
		private var _ruler:Shape;
		private var _cover:Sprite;
		private var _covered:Boolean;
		private var _initialWatermarkY:Number = 0, _currentWatermarkY:Number = 0;
		private var _capacity:Number, _volume:Number, _realWidth:Number, _realHeight:Number; // in cm
		private var _width:Number, _height:Number; // in pixels
		
		public function get pxWidth():Number{return _width;}
		public function get pxHeight():Number{return _height;}
		public function get pxCircHeight():Number{return _width*HRATIO;}
		
		public function get covered():Boolean{return _covered;}
		public function set covered(b:Boolean):void{if(b){cover();}else{uncover();}}
		public function get realHeight ():Number{return _height;}
		public function get realWidth ():Number{return _width;}
		public function get capacity ():Number{return _capacity;}
		public function get volume ():Number{return _volume;}
		public function set volume (v:Number):void {_volume=v; redrawWater();}
		private var _tobj:MovieClip;
		
		
		/** Enter the capacity, width and inital volume in ml (or cm^3) */
		public function Beaker(capacity:Number, width:Number, volume:Number, volumeConversionFactor:Number=1, rulerDivisions:uint = 10)
		{
			this._capacity = capacity;
			this._realWidth = width;
			//this._capacity = Math.PI * Math.pow(_width/2, 2) * _height;
			this._realHeight = this._capacity/(Math.PI*Math.pow(_realWidth/2,2));
			this._width = _realWidth * DensityItem.CM_TO_PX;
			this._height = _realHeight * DensityItem.CM_TO_PX;
			this._volume = volume;
			// draw back of beaker
			var HRATIO:Number = .25;
			_back = new Shape();
			_back.graphics.beginFill(0xFFFFFF, .25);
			_back.graphics.lineStyle(_width/50, 0xAAAAAA, 1);
			Beaker.drawSemiEllipseUp(_back.graphics, 0, 0, _width, _width*HRATIO/2);
			//Beaker.drawSemiEllipseUp(_back.graphics, 0, _height-_width*HRATIO, _width, _width*HRATIO/2, true);
			Beaker.drawSemiEllipseUp(_back.graphics, 0, _height, _width, _width*HRATIO/2, true);
			_back.graphics.endFill();
			_back.graphics.beginFill(0xFFFFFF, .25);
			//_back.graphics.drawEllipse(0, _height-_width*HRATIO, _width, _width*HRATIO);
			_back.graphics.drawEllipse(0, _height, _width, _width*HRATIO);
			_back.graphics.endFill();
			
			
			_front = new Shape();
			_front.graphics.lineStyle(_width/50, 0xAAAAAA, 1);
			_front.graphics.beginFill(0xFFFFFF, .25);
			Beaker.drawSemiEllipseDown(_front.graphics, 0, _width*HRATIO/2, _width, _width*HRATIO/2);
			//Beaker.drawSemiEllipseDown(_front.graphics, 0, _height-_width*HRATIO/2, _width, _width*HRATIO/2, true);
			Beaker.drawSemiEllipseDown(_front.graphics, 0, _height+_width*HRATIO/2, _width, _width*HRATIO/2, true);
			_front.graphics.endFill();
			
			// Draw water
			_waterFront = new Shape();
			_waterBack = new Shape();
			
			// Draw ruler
			_ruler = new Shape();
			_ruler.graphics.lineStyle(_width/100, 0x444444);
			// draw divisions of ruler
			for (var i:uint=0; i <= rulerDivisions; i++)
			{
				_ruler.graphics.moveTo(_width, _width*HRATIO/2+_height*i/rulerDivisions);
				_ruler.graphics.lineTo(_width+_width/10, _width*HRATIO/2+_height*i/rulerDivisions);
			}
			
			this.addChild (_back);
			this.addChild (_waterBack);
			this.addChild(_front);
			this.addChild(_waterFront);
			this.addChild(_ruler);
			
			// Draw cover
			covered = true;
			_initialWatermarkY = (_height)*(1-_volume/_capacity) + _width*HRATIO/2;
			_currentWatermarkY = _initialWatermarkY;
			redrawWater();
			
			_objects = new Array();
		}
		
		/** If user clicks on density button go through each flicable mc and show density */
		public function showDensity ():void
		{
			for each (var dmc:DroppableMC  in _objects)
			{
				dmc.hideDensity();
			}
		}
		public function hideDensity ():void
		{
			for each (var dmc:DroppableMC in _objects)
			{
				dmc.showDensity();
			}
		}
		
		
		
		
		/** This function is used to add a new flickable object (or shape) to the panel, at specified location */
		public function addDroppableMC (type:String, mass:Number, volume:Number=0)
		{
			if (_objects.length < MAX_OBJECTS)
			{
				if (type == "RectPrism")
				{
					var dmc:DroppableMC = new DroppableShape ( "RectPrism", mass, 1.5, 1, 2);
					this.addChild(dmc);
					var initpoint:Point = new Point(_objects.length*_width/MAX_OBJECTS + (_width/MAX_OBJECTS - dmc.width)/2, -_cover.height/2);
					dmc.initPoint = initpoint;
					_objects.push(dmc);
				}
			}
		}
		/** Is point above or below fluid?  Get density */
		public function densityAtPoint(p:Point):Number
		{
			if (p.y > _height)
			{
				return 1000000;
			} else if (p.y > (_height)*(1-_volume/_capacity))
			{
				return FLUID_DENSITY;
			} else
			{
				return 0;
			}
		}
		
		/** This tells us how much of the full volume should be accounted for when calculating the water's force up */
		public function percentUnderFluid(dmc:DroppableMC):Number
		{
			//trace ("??? submerged", dmc.y, dmc.pxHeight );
			if (dmc.y > _initialWatermarkY)
			{
				return 1;
			} else if (dmc.y + dmc.pxHeight < _initialWatermarkY)
			{
				return 0;
			} else
			{
				//trace("half submerged", watermarkY, dmc.y, (watermarkY - dmc.y) / dmc.pxHeight, (dmc.y + dmc.pxHeight - watermarkY)/ dmc.pxHeight);
				return ( (dmc.y + dmc.pxHeight - _initialWatermarkY)/ dmc.pxHeight);
			}
		}
		
		/** Covers the top of the cylinder. */
		public function cover():void
		{
			if (!_covered)
			{
				if (_cover == null || _cover.parent != this)
				{
					_cover = new Sprite();
					_cover.graphics.lineStyle(_width/40, 0x888888);
					_cover.graphics.beginFill(0xCCCCCC);
					// draw a skewed rectangle
					_cover.graphics.moveTo(0, 0);
					_cover.graphics.lineTo(_width+_width/8, 0);
					_cover.graphics.lineTo(_width, _width*HRATIO-_width*1*HRATIO/8);
					_cover.graphics.lineTo(-_width/8, _width*HRATIO-_width*1*HRATIO/8);
					//_cover.graphics.lineTo(-_width/8, 0);
					_cover.graphics.endFill();
					this.addChild(_cover);
				}
				// add event listener so that cover can be removed
				_cover.addEventListener( MouseEvent.MOUSE_DOWN, handleCoverMouseDown);
				_covered = true;
			}
		}
		private function handleCoverMouseDown (event:MouseEvent):void
		{
			_cover.removeEventListener( MouseEvent.MOUSE_DOWN, handleCoverMouseDown);
			uncover();				
		}
		
		/** Uncovers the top of the cylinder */
		public function uncover():void
		{
			if (_covered)
			{
				this.removeChild(_cover);
				_covered = false;
				// go through all objects and start dropping
				for each (var dmc:DroppableMC in _objects)
				{
					// place dmc just in front of the background objects
					this.removeChild(dmc);
					this.addChildAt(dmc, NUM_BACKGROUND_OBJECTS);
					dmc.drop();
				}
				this.addEventListener(Event.ENTER_FRAME, handleFrame);
			}
		}
////////////////////////////////////////// DRAWING ///////////////////////////////////////////		
		/** When dropping initiated update watermark and redraw if changed. */
		private function handleFrame (event:Event):void
		{
			var prevWatermarkY:Number = _currentWatermarkY; 
			// start with simply copacity, volume, and height of ellipse
			var _totalvolume:Number = _volume;
			// go through each object in water and factor in the total volume of water and all objects in watermark
			var stillDropping:Boolean = false;
			for each (var dmc:DroppableMC in _objects)
			{
				if (dmc.isDropping)
				{
					stillDropping = true;
				}
				_totalvolume += dmc.currentPercentInFluid * dmc.volume;
			}
			
			_currentWatermarkY = (_height)*(1-_totalvolume/_capacity) + _width*HRATIO/2;
			if (_currentWatermarkY != prevWatermarkY)
			{
				redrawWater();
			}			
			if (!stillDropping)
			{
				trace ("STOP DROPPING");
				this.removeEventListener(Event.ENTER_FRAME, handleFrame);
			}
				
		}
		
		
		
		/** Draws water with current volume level */
		private function redrawWater():void
		{
			_waterBack.graphics.clear();
			_waterBack.graphics.lineStyle(_width/100, 0xAACCFF, .5);
			_waterBack.graphics.beginFill(0xAACCFF, .25);
			Beaker.drawSemiEllipseUp(_waterBack.graphics, 0, _currentWatermarkY - _width*HRATIO/2, _width, _width*HRATIO/2);
			Beaker.drawSemiEllipseUp(_waterBack.graphics, 0, _height, _width, _width*HRATIO/2, true);
			_waterBack.graphics.endFill();
			_waterBack.graphics.beginFill(0xAACCFF, .25);
			_waterBack.graphics.drawEllipse(0, _height, _width, _width*HRATIO);
			_waterBack.graphics.endFill();
			
			_waterFront.graphics.clear();
			_waterFront.graphics.lineStyle(_width/100, 0xAACCFF, .5);
			_waterFront.graphics.beginFill(0xAACCFF, .25);
			Beaker.drawSemiEllipseDown(_waterFront.graphics, 0, _currentWatermarkY, _width, _width*HRATIO/2);
			Beaker.drawSemiEllipseDown(_waterFront.graphics, 0, _width*HRATIO/2+_height, _width, _width*HRATIO/2, true);
			_waterFront.graphics.endFill();
			
		}
		
		
		
		/** Draw the arc of a semi-ellipse.  Inverted is used to connect two semi-ellipses to form a continuous shape in between. */
		public static function drawSemiEllipseUp (g:Graphics, ix:Number, iy:Number, width:Number, height:Number, invert:Boolean=false):void
		{
			var bx:Number = .085;
			var by:Number = .065
			if (!invert)
			{
				g.moveTo(ix, iy+height);
				g.curveTo(ix+bx*width,  iy+by*height, ix+width/2, iy);
				g.curveTo(ix+width-bx*width, iy+by*height, ix+width, iy+height);
			} else
			{
				g.lineTo(ix+width, iy+height);	
				g.curveTo(ix+width-bx*width, iy+by*height, ix+width/2, iy);
				g.curveTo(ix+bx*width,  iy+by*height, ix, iy+height);
			}
			
		}
		public static function drawSemiEllipseDown (g:Graphics, ix:Number, iy:Number, width:Number, height:Number, invert:Boolean=false):void
		{
			var bx:Number = .09;
			var by:Number = .065;
				
			if (!invert)
			{
				g.moveTo(ix, iy);
				g.curveTo(ix+bx*width,  iy+height-by*height, ix+width/2, iy+height);
				g.curveTo(ix+width-bx*width, iy+height-by*height, ix+width, iy);
			} else
			{
				g.lineTo(ix+width, iy);	
				g.curveTo(ix+width-bx*width, iy+height-by*height, ix+width/2, iy+height);
				g.curveTo(ix+bx*width,  iy+height-by*height, ix, iy);
			}
			
		}
	}
}